package lift_simulator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
/**
 * ビルに存在するエレベータを表現するオブジェクトです。<br>
 * このオブジェクトは基本的な情報や格納している(搭乗している)タスクのリストの他に、
 * {@link Aim}オブジェクト：目的を持っています。<br>Liftオブジェクトは単位時間(１秒)ごとにこの
 * 目的に従って移動やドアの開閉を行います。<br>目的オブジェクトは{@link LiftManager#getAim(Lift)}から、
 * 行動の区切り毎に取得します。
 * @author Yone6379univ
 *
 */
public class Lift {
	/**
	 * 保持している(搭乗している)タスクのリフト
	 * @see Task
	 */
    private ArrayList<Task> tasks;
    /**
     * ドアが開いているか。主にリフトの行動を選択する際に参照します。
     */
    private boolean isOpen;
    /**
     * リフトの積載可能人数です。プログラムを起動する際に指定します。
     */
    private int capacity;
    /**
     * 現在の階です。
     * ２秒で１階移動しますが、１秒後は0.5移動します。
     */
    private double currentFloor;
    /**
     * 目的オブジェクトです。このオブジェクトに従ってリフトが動作します。
     */
    private Aim aim;
    /**
     * 識別番号です。1から始まります。
     */
    private int id;
    /**
     * 現在待っている時間です。主にドアを開いてから閉じるまでの待機時間に利用されます。
     */
    private int waitTime;
    private CsvManager csvManager;
    private Clock clock;

    public Lift(int capacity, double currentFloor, int id, CsvManager csvManager, Clock clock) {
    	this.clock=clock;
    	this.csvManager=csvManager;
		this.capacity = capacity;
		this.currentFloor = currentFloor;
		this.id = id;
		this.isOpen=false;
		this.aim=null;
		this.waitTime=0;
		this.tasks = new ArrayList<Task>();
	}
    /**
     * 目的の方向へ向かいます
     */
	public void move(){
		waitTime=0;
		currentFloor+=aim.getDirection().getValue();
    }

    public void log(){
    }
    
    /**
     * ドアを開け、指定されたIDのタスクを放出します。
     * TODO 乗り継ぎを利用する場合、単純に解放するだけではNG
     */
    public void open(){
    	isOpen=true;
    	for(int id:aim.getDescend()){
    		Task targetTask = getTaskByID(id);
    		tasks.remove(targetTask);
    	}
    	LiftLog log = new LiftLog(this, true);
		try {
			getCsvManager().log(log);
		} catch (IOException e) {
			e.printStackTrace();
		}
		//System.out.println("open");
    }
    /**
     * ドアを閉めると同時に、指定されたIDのタスクを取得します。
     */
    public void close(Floor floor){
    	isOpen=false;
    	for(int id:aim.getBoard()){
    		Task targetTask = floor.getTaskByID(id,true);
    		tasks.add(targetTask);
    		floor.removeTask(targetTask);
    	}
    	LiftLog log = new LiftLog(this, false);
		try {
			getCsvManager().log(log);
		} catch (IOException e) {
			e.printStackTrace();
		}
		//System.out.println("close");
    }
    /**
     * リフトを待機させます。
     */
    public void waitLift(){
    	waitTime++;
    }
    
    /**
     * リフトに格納されている指定されたIDのタスクを返します。
     * タスクが格納されていない場合、nullが返ります。
     * @param id
     * @return
     */
    private Task getTaskByID(int id){
    	for(Task task : tasks){
    		if(task.getId()==id)
    			return task;
    	}
    	return null;
    }

	public ArrayList<Task> getTasks() {
		return tasks;
	}

	public boolean isOpen() {
		return isOpen;
	}

	public int getCapacity() {
		return capacity;
	}

	public double getCurrentFloor() {
		return currentFloor;
	}

	public Aim getAim() {
		return aim;
	}

	public void setAim(Aim aim) {
		this.aim = aim;
	}

	public int getId() {
		return id;
	}

	public int getWaitTime() {
		return waitTime;
	}
	
	/**
	 * シミュレーターの入出力を扱うオブジェクトを返します。<br>
	 * 静的メソッドとしての取得も可能ですが、
	 * 今後の拡張(ビルごとに入出力が異なる場合と同じ場合)も考えて委譲的なメソッドを用意しています。
	 * フィールドに入れない理由はただの自分のオブジェクト構成のイメージです。
	 * @return
	 */
	public CsvManager getCsvManager(){
		return csvManager;
	}
	
	public Clock getClock(){
		return clock;
	}
	/**
	 * タスクを持っていない時、trueを返します。
	 * @return
	 */
	public boolean hasNoTask() {
		return tasks.size()==0;
	}
}
